#ifndef NJF_RASTER_FIXED_H
#define NJF_RASTER_FIXED_H

#include <stdint.h>
#include <math.h>
#include "raster-util.h"

/*
    a representation of non-integer real numbers
    using only integer arithmetic

    has 16bits of precision
*/
typedef int_fast32_t Fixed;

#define FIXED_ONE       0x00010000
#define FIXED_HALF      0x00008000

/* for defining your own fixed constants */
#if defined(__cplusplus) && __cplusplus >= 201103
constexpr Fixed FIXED_I(int val)
{
    return (Fixed)(val << 16);
}
constexpr Fixed FIXED_FRAC(int num, int denom)
{
    return (Fixed)((num << 16) / denom);
}
constexpr Fixed FIXED_F(float f)
{
    return (Fixed)(f * 65536.f);
}

Fixed operator "" _fx(int i){ return (Fixed)(i << 16); }
Fixed operator "" _fx(float f){ return (Fixed)(f * 65536.f); }
#else
/*  GCC warns about use of FIXED_F where an integer constant is required by the standard, but doesn't break
    Visual C++ was oddly quiet about it */
#define FIXED_I(val)                (Fixed)((val) << 16)
#define FIXED_FRAC(num, denom)      (Fixed)(((num) << 16) / (denom))
#define FIXED_F(val)                (Fixed)((val) * 65536.f)
#endif

/*
    has half the precision of Fixed (8 fraction bits)
    makes for faster arithmetic when multiplication or division are necessary
*/
typedef uint_fast32_t Half;

#define HALF_ONE        0x00000100
#define HALF_HALF       0x00000080

/* for defining your own hslf constants */
#if defined(__cplusplus) && __cplusplus >= 201103
constexpr Half HALF_I(int val)
{
    return (Half)(val << 8);
}
constexpr Half HALF_FRAC(int num, int denom)
{
    return (Half)((num << 8) / denom);
}
constexpr Half HALF_F(float f)
{
    return (Half)(f * 256.f);
}

Half operator "" _hx(int i){ return (Fixed)(i << 8); }
Half operator "" _hx(float f){ return (Fixed)(f * 256.f); }
#else
/*  GCC warns about use of HALF_F where an integer constant is required by the standard, but doesn't break
    Visual C++ was oddly quiet about it */
#define HALF_I(val)                (Half)((val) << 8)
#define HALF_FRAC(num, denom)      (Half)(((num) << 8) / (denom))
#define HALF_F(val)                (Half)((val) * 256.f)
#endif

/* convert an integer into a Fixed value */
BEST_INLINE Fixed IntToFixed(int val)
{
    return val << 16;
}
BEST_INLINE Half IntToHalf(int i)
{
    return i << 8;
}

/* convert a Fixed value into an integer */
BEST_INLINE int FixedToInt(Fixed val)
{
    return val >> 16;
}
BEST_INLINE int HalfToInt(Half h)
{
    return h >> 8;
}

/* convert a float into a Fixed value */
BEST_INLINE Fixed FloatToFixed(float val)
{
    Fixed ret;
    float whole = floorf(val);
    val -= whole;
    ret = ((int)whole) << 16;
    ret += ((int)(val * 65536.f));
    return ret;
}
BEST_INLINE Half FloatToHalf(float val)
{
    return FloatToFixed(val) >> 8;
}
/* convert a Fixed into a float */
BEST_INLINE float FixedToFloat(Fixed val)
{
    float ret = (val >> 16) * 1.f;
    ret += (val & 0xFFFF) / 65536.f;
    return ret;
}
BEST_INLINE float HalfToFloat(Half val)
{
    float ret = (val >> 8) * 1.f;
    ret += (val & 0xFF) / 65536.f;
    return ret;
}
/* convert between fixed and half */
BEST_INLINE Half FixedToHalf(Fixed f)
{
    return f >> 8;
}
BEST_INLINE Fixed HalfToFixed(Half h)
{
    return h << 8;
}

/* convert a fixed number to ascii for use with printf etc
    will write a maximum of 1(sign) + 5(whole) + 1(dot) + 9(fraction) + 1(null terminator) = 17
        characters into buffer
    width limits the number of characters after the '.' to write. 9 is the highest reasonable value
    returns number of characters written -1 (the null terminator isn't counted).
*/
EXTERNC int __cdecl FixedToAscii(Fixed val, char *buf, int bufsz, int width);

/* arithmetic functions */

/* ret = a + b; */
BEST_INLINE Fixed FixedAdd(Fixed a, Fixed b)
{
    return a + b;
}
BEST_INLINE Fixed FixedAddInt(Fixed a, int b)
{
    return a + (b << 16);
}
BEST_INLINE Half HalfAdd(Half a, Half b)
{
    return a + b;
}
BEST_INLINE Half HalfAddInt(Half a, int b)
{
    return a + (b << 8);
}

/* ret = a - b; */
BEST_INLINE Fixed FixedSub(Fixed a, Fixed b)
{
    return a - b;
}
BEST_INLINE Fixed FixedSubInt(Fixed a, int b)
{
    return a - (b << 16);
}
BEST_INLINE Half HalfSub(Half a, Half b)
{
    return a - b;
}
BEST_INLINE Half HalfSubInt(Half a, int b)
{
    return a - (b << 8);
}
/* ret = a * b */
BEST_INLINE Fixed FixedMul3s(Fixed a, Fixed b, Fixed c)
{
    int64_t res = (int64_t)a * (int64_t)b * (int64_t)c;
    return (Fixed)(res >> 32);
}
#if defined(_MSC_VER)
/* without use of this intrinsic, Visual C++ wants to call its __allmul support function,
    which performs a very slow full 64-bit multiply */
BEST_INLINE Fixed FixedMul(Fixed a, Fixed b)
{
    __int64 ret = __emul(a, b);
    return ret >> 16;
}
#elif !defined(SLOWMUL32_64)
BEST_INLINE Fixed FixedMul(Fixed a, Fixed b)
{
    int64_t res = (int64_t)(a) * b;
    return (Fixed)(res >> 16);
}
#else
BEST_INLINE Fixed FixedMul(Fixed a, Fixed b)
{
    Fixed ret0 = (a >> 16) * b;
    Fixed ret1 = ((a - (a & 0xFFFF0000)) * (b - (b & 0xFFFF0000))) >> 16;
    Fixed ret2 = ((a - (a & 0xFFFF0000)) * (b >> 16));
    return ret0 + ret1 + ret2;
}
#endif
BEST_INLINE Fixed FixedMulLossy(Fixed a, Fixed b)
{
    /* should be faster on 32-bit archs
    will lose the bottom 8 bits */
    return ((a>>8) * (b>>8));
}
BEST_INLINE Fixed FixedMulInt(Fixed a, int b)
{
    return a * b;
}
BEST_INLINE Half HalfMul(Half a, Half b)
{
    return (a * b) >> 8;
}
BEST_INLINE Half HalfMulInt(Half a, int b)
{
    return a * b;
}
BEST_INLINE Fixed FixedMulHalf(Fixed a, Half b)
{
    int64_t res = (int64_t)a * (int64_t)(b << 8);
    return (Fixed)(res >> 16);
}
BEST_INLINE Fixed FixedMulHalfLossy(Fixed a, Half b)
{
    /* should be faster on 32-bit archs
    will lose the bottom 8 bits */
    return (a >> 8) * b;
}
/* ret = a % b */
BEST_INLINE Fixed FixedMod(Fixed a, Fixed b)
{
    return a % b;
}
BEST_INLINE Fixed FixedModInt(Fixed a, int b)
{
    return a % (b << 16);
}
BEST_INLINE Half HalfMod(Half a, Half b)
{
    return a % b;
}
BEST_INLINE Half HalfModInt(Half a, int b)
{
    return a % (b << 8);
}
BEST_INLINE Fixed FixedModHalf(Fixed a, Half b)
{
    return a % (b << 8);
}
/* ret = a / b */
BEST_INLINE Fixed FixedDiv(Fixed a, Fixed b)
{
    int_fast64_t alarge = ((int_fast64_t)a << 16);
    return (Fixed)(alarge / b);
}
BEST_INLINE Fixed FixedDivLossy(Fixed a, Fixed b)
{
    /* should be faster on 32-bit archs
        will lose the top 8 bits of a and the bottom 8 bits of b
        only use when certain |a| <= 255
    */
    return (((a<<8)&0xFFFF0000) / (b >> 8));
}
BEST_INLINE Fixed FixedDivInt(Fixed a, int b)
{
    return a / b;
}
/* quickly invert a fixed point value with only one bit of precision loss */
BEST_INLINE Fixed FixedInvLossy(Fixed a)
{
    /* negate is necessary because FIXED_ONE << 15 sets the sign bit */
    return -((FIXED_ONE << 15) / a) << 1;
}
BEST_INLINE Half HalfDiv(Half a, Half b)
{
    return (a << 8) / b;
}
BEST_INLINE Half HalfDivInt(Half a, int b)
{
    return a / b;
}
BEST_INLINE Fixed FixedDivHalf(Fixed a, Half b)
{
    int_fast64_t alarge = ((int_fast64_t)a << 8);
    return (Fixed)(alarge / b);
}

BEST_INLINE Fixed FixedFloor(Fixed val)
{
    return val & 0xFFFF0000;
}

BEST_INLINE Fixed FixedCeil(Fixed val)
{
    return FixedFloor((val - 1) + FIXED_ONE);
}

BEST_INLINE Fixed FixedRound(Fixed val)
{
    return FixedFloor(val + FIXED_HALF);
}





/* ret = a + b */


#endif /* NJF_RASTER_FIXED_H */
